package com.sunlands.qdcy.deskmate.service;

import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayApiException;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.sunlands.qdcy.deskmate.client.UserCenterService;
import com.sunlands.qdcy.deskmate.dao.pojo.OrderRecordDO;
import com.sunlands.qdcy.deskmate.dao.repository.OrderRecordRepository;
import com.sunlands.qdcy.deskmate.enums.AlipayTradeStatus;
import com.sunlands.qdcy.deskmate.enums.OrderStatus;
import com.sunlands.qdcy.deskmate.enums.PayType;
import com.sunlands.qdcy.deskmate.enums.YN;
import com.sunlands.qdcy.deskmate.exception.BusinessException;
import com.sunlands.qdcy.deskmate.exception.SendExceptionUtil;
import com.sunlands.qdcy.deskmate.util.DozerMapper;
import com.sunlands.qdcy.deskmate.vo.BusinessResult;
import com.sunlands.qdcy.deskmate.vo.feign.VipUserVO;
import com.sunlands.qdcy.deskmate.vo.feign.VipVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.omg.CORBA.UNKNOWN;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.Predicate;
import java.util.*;
import java.util.concurrent.TimeUnit;

import static com.sunlands.qdcy.deskmate.config.RedisConfig.CacheKeys.WAITING_PAY_ORDER_OUT_TRADE_NO;
import static com.sunlands.qdcy.deskmate.constant.CommonResultMessage.REPEAT_ORDER;
import static com.sunlands.qdcy.deskmate.service.AlipayService.ALIPAY_NO_EXIST;
import static com.sunlands.qdcy.deskmate.service.AlipayService.ALIPAY_SUCCESS;

/**
 * Created with meng.
 * User: meng5
 * Date: 2019-04-24
 * Description:
 */
@Slf4j
@Service
public class OrderRecordService {

    @Autowired
    private OrderRecordRepository orderRecordRepository;

    @Lazy
    @Autowired
    private AlipayService alipayService;
    @Autowired
    private UserCenterService userCenterService;
    @Autowired
    private SendExceptionUtil sendExceptionUtil;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private WechatPayService wechatPayService;
    @Autowired
    private PostgraduateWechatPayService postgraduateWechatPayService;
    @Autowired
    private ApplePayService applePayService;


    /**
     * 保存-更新订单，所有接口更新订单都通过这个方法
     *
     * @param info
     * @return
     */
    @Transactional(rollbackFor = Exception.class, isolation = Isolation.REPEATABLE_READ)
    public OrderRecordDO save(OrderRecordDO info) {
        if (info == null || (StringUtils.isBlank(info.getOutTradeNo()))) {
            throw new BusinessException("订单信息不全！");
        }
        OrderRecordDO record = findByOutTradeNo(info.getOutTradeNo());
        if (Objects.nonNull(record)) {
            DozerMapper.getInstance().map(info, record);
            record = orderRecordRepository.save(record);
        } else {
            record = orderRecordRepository.save(info);
        }

        //添加未支付订单60秒缓存，用于轮询使用
        if (OrderStatus.PAY_WAITING.name().equals(record.getOrderStatus())) {
            redisTemplate.opsForValue().set(getWaitingPayOrderKey(record.getOutTradeNo()), record.getOrderStatus(), 3 * 60, TimeUnit.SECONDS);
        } else {
            redisTemplate.delete(getWaitingPayOrderKey(record.getOutTradeNo()));
        }
        return record;
    }


    public static String getWaitingPayOrderKey(String outTradeNo) {
        return WAITING_PAY_ORDER_OUT_TRADE_NO + outTradeNo;
    }

    /**
     * 订单发货
     *
     * @param outTradeNo
     */
    @Transactional(rollbackFor = Exception.class)
    public void sendGoods(String outTradeNo) {
        int retry = 3;
        try {
            while (retry > 0) {
                OrderRecordDO orderRecord = findByOutTradeNo(outTradeNo);
                if (!OrderStatus.PAY_SUCCESS.name().equals(orderRecord.getOrderStatus())) {
                    Thread.sleep(500);
                    retry--;
                } else {
                    VipVO vipVO = VipVO.builder().orderNo(outTradeNo)
                            .setMealId(orderRecord.getGoodsNumber())
                            .userId(orderRecord.getUserId())
                            .operateType(1) //1续费
                            .build();
                    log.info("支付成功订单调用户服务改vip状态，params={}", JSON.toJSONString(vipVO));
                    BusinessResult<VipUserVO> result = userCenterService.openVip(vipVO);
                    if (result.getCode().equals(REPEAT_ORDER.code)) {
                        log.info("重复发货 outTradeNo = {}", outTradeNo);
                    }
                    //更新订单状态为已发货
                    orderRecord.setSend(YN.YES.code);
                    save(orderRecord);
                    return;
                }
            }
            log.error("非支付成功订单 不能发货 outTradeNo = {}", outTradeNo);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 订单退货
     *
     * @param outTradeNo
     */
    @Transactional(rollbackFor = Exception.class)
    public void returnGoods(String outTradeNo) {
        OrderRecordDO orderRecord = findByOutTradeNo(outTradeNo);
        if (!OrderStatus.PAY_SUCCESS.name().equals(orderRecord.getOrderStatus())) {
            log.error("非支付成功订单, 不能退货 outTradeNo = {}", outTradeNo);
            return;
        }
        VipVO vipVO = VipVO.builder().orderNo(outTradeNo)
                .setMealId(orderRecord.getGoodsNumber())
                .userId(orderRecord.getUserId())
                .operateType(2) //退费
                .build();
        log.info("退货，params={}", JSON.toJSONString(vipVO));
        BusinessResult<VipUserVO> result = userCenterService.closeVip(vipVO);
        if (result.getCode().equals(REPEAT_ORDER.code)) {
            log.info("重复发货 outTradeNo = {}", outTradeNo);
        }
        //更新订单状态为已发货
        orderRecord.setSend(YN.NO.code);
        orderRecord.setOrderStatus(OrderStatus.RETURN.name());
        orderRecord.setReturnTime(new Date());
        save(orderRecord);
    }


    /**
     * 根据商户订单号查询订单
     *
     * @param outTradeNo 商户订单号
     * @return
     */
    public OrderRecordDO findByOutTradeNo(String outTradeNo) {
        if (StringUtils.isBlank(outTradeNo)) {
            throw new BusinessException("商户订单号为空！");
        }
        return orderRecordRepository.findByOutTradeNo(outTradeNo);
    }

    public List<OrderRecordDO> findAllByUserId(Long userId) {
        OrderRecordDO orderRecordDO = OrderRecordDO.builder().userId(userId).build();
        return orderRecordRepository.findAll(Example.of(orderRecordDO));
    }


    /**
     * 未支付的订单查询
     */
    public static final int minute = 60 * 24;

    public Page<OrderRecordDO> findPayWaitingOrders(Pageable pageable) {
        //获取一小个小时前数据
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        calendar.set(Calendar.MINUTE,
                calendar.get(Calendar.MINUTE) - minute);

        Specification<OrderRecordDO> specification = (root, query, cb) -> {
            List<Predicate> list = new ArrayList<>();
            list.add(cb.equal(root.get("orderStatus"), OrderStatus.PAY_WAITING.name()));
            list.add(cb.greaterThan(root.get("createTime"), calendar.getTime()));
            return cb.and(list.toArray(new Predicate[list.size()]));
        };
        return orderRecordRepository.findAll(specification, pageable);
    }


    /**
     * 未支付的订单查询
     */
    public static final int day30minute = 60 * 24 * 30;

    /**
     * 查询已支付和发货的订单
     *
     * @param pageable pageable
     * @return Page
     */
    public Page<OrderRecordDO> findPaySuccessOrders(Pageable pageable) {
        //获取一小个小时前数据
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        calendar.set(Calendar.MINUTE,
                calendar.get(Calendar.MINUTE) - day30minute);

        Specification<OrderRecordDO> specification = (root, query, cb) -> {
            List<Predicate> list = new ArrayList<>();
            list.add(cb.equal(root.get("orderStatus"), OrderStatus.PAY_SUCCESS.name()));
            list.add(cb.equal(root.get("payType"), PayType.IAP.name()));
            list.add(cb.equal(root.get("send"), YN.YES.code));
            list.add(cb.greaterThan(root.get("createTime"), calendar.getTime()));
            return cb.and(list.toArray(new Predicate[list.size()]));
        };
        return orderRecordRepository.findAll(specification, pageable);
    }

    /**
     * 查询未发货的订单
     *
     * @param pageable pageable
     * @return Page
     */
    public Page<OrderRecordDO> findWaitingSendGoodsOrders(Pageable pageable) {
        Specification<OrderRecordDO> specification = (root, query, cb) -> {
            List<Predicate> list = new ArrayList<>();
            list.add(cb.equal(root.get("orderStatus"), OrderStatus.PAY_SUCCESS.name()));
            list.add(cb.equal(root.get("send"), YN.NO.code));
            return cb.and(list.toArray(new Predicate[list.size()]));
        };
        return orderRecordRepository.findAll(specification, pageable);
    }

    /**
     * 拉取待支付订单状态，如果已经支付，发货，定时任务使用
     *
     * @param outTradeNo outTradeNo
     */
    public void pullOrderStatusAndDealOrder(String outTradeNo) {
        OrderRecordDO order = findByOutTradeNo(outTradeNo);

        if (PayType.WECHAT.name().equals(order.getPayType())) {
            wechatPayService.closeOrderFromWechat(order);

        } else if (PayType.ALIPAY.name().equals(order.getPayType())) {
            aliPayOrderHandler(order);

        } else if (PayType.IAP.name().equals(order.getPayType())) {
            applePayService.closeOrder(order);

        } else if(PayType.WECHAT_POSTGRADUATE.name().equals(order.getPayType())){
            postgraduateWechatPayService.closeOrderFromWechat(order);

        }else {
            throw new UNKNOWN("UNKNOW PAY_TYPE");
        }
    }



    @Transactional(rollbackFor = Exception.class)
    public void closeOrder(String outTradeNo) {
        OrderRecordDO orderRecordDO = findByOutTradeNo(outTradeNo);
        closeOrder(orderRecordDO);
    }

    @Transactional(rollbackFor = Exception.class)
    public void closeOrder(OrderRecordDO order) {
        order.setOrderStatus(OrderStatus.CANCEL.name());
        save(order);
    }

    private void aliPayOrderHandler(OrderRecordDO order) {
        AlipayTradeQueryResponse response = alipayService.pullOrderStatus(order.getOutTradeNo());
        if (response.getCode().equals(ALIPAY_NO_EXIST)) {
            //支付宝订单设置的超时时间为1小时，如果超过两个小时，依然查询不到支付宝订单，将本地订单关闭
            long time = System.currentTimeMillis() - order.getCreateTime().getTime();
            log.debug("订单创建时间 {}", time);
            if (time >= (1000 * 60 * 60 + 100000)) {
                //取消订单
                log.info("支付宝订单未查询到，取消本地订单 订单id = {}", order.getOutTradeNo());
                closeOrder(order);
            }
            return;
        }

        if (!response.getCode().equals(ALIPAY_SUCCESS)) {
            log.error("支付宝订单定时检查，不处理的未知返回状态，response = {}", response);
            sendExceptionUtil.sendException("支付宝订单定时检查，不处理的未知返回状态，response = " + response, new AlipayApiException(""));
        }

        //发货
        if (AlipayTradeStatus.TRADE_SUCCESS.toString().equals(response.getTradeStatus())
                || AlipayTradeStatus.TRADE_FINISHED.toString().equals(response.getTradeStatus())) {
            log.info("支付宝订单查询到已经支付，发货处理 response = {}", response);

            //修改订单状态为已支付
            order.setOrderStatus(OrderStatus.PAY_SUCCESS.name());
            save(order);

            //发货
            sendGoods(order.getOutTradeNo());

        } else if (AlipayTradeStatus.TRADE_CLOSED.toString().equals(response.getTradeStatus())) {
            log.info("支付宝订单已经取消，取消本地订单 response = {}", response);
            closeOrder(order);

        } else if (AlipayTradeStatus.WAIT_BUYER_PAY.toString().equals(response.getTradeStatus())) {
            log.debug("支付宝订单待支付，不处理 response = {}", response);
        } else {
            log.warn("未知未处理支付宝返回类型 response = {}", response);
        }
    }

    /**
     * 根据订单号查询订单
     *
     * @param outTradeNo 支付宝订单号
     * @return OrderRecordDO
     */
    public OrderRecordDO findByTradeNo(String outTradeNo) {
        if (StringUtils.isBlank(outTradeNo)) {
            throw new BusinessException("支付宝订单号为空！");
        }
        return orderRecordRepository.findByOutTradeNo(outTradeNo);
    }

}